#!/bin/bash

# SpringBoot2通用启动脚本
# 作者: 丁文
# 时间: 2025-04-09 22:00:05

# 默认配置
DEFAULT_APP_NAME="treasure-springboot2-example"
DEFAULT_CONFIG_PATH="/app/treasure-springboot2-example/config"
DEFAULT_DEBUG_PORT=5005
DEFAULT_CONSOLE_LOG=false
DEFAULT_UNPACK=true
DEFAULT_LOG_PATH="logs"
DEFAULT_GC_LOG_PATH="logs/gc"
DEFAULT_DUMP_DIR="logs/dump"
DEFAULT_SHUTDOWN_URL="http://localhost:8443/actuator/shutdown"
DEFAULT_SHUTDOWN_RETRIES=5
DEFAULT_SHUTDOWN_WAIT=3
# 默认备份目录
DEFAULT_BACKUP_DIR="backup"  
DEFAULT_BACKUP_FILES="treasure-springboot2-example.jar lib"
DEFAULT_START_CLASS="top.dingwen.io.treasure.example.ExampleApplication" 
DEFAULT_ADDITIONAL_PARAMS=""

# 用户配置（可以通过环境变量覆盖）
APP_NAME="${APP_NAME:-$DEFAULT_APP_NAME}"
CONFIG_PATH="${CONFIG_PATH:-$DEFAULT_CONFIG_PATH}"
CONSOLE_LOG="${CONSOLE_LOG:-$DEFAULT_CONSOLE_LOG}"
DEBUG_PORT="${DEBUG_PORT:-$DEFAULT_DEBUG_PORT}"
UNPACK="${UNPACK:-$DEFAULT_UNPACK}"

# 启动类默认配置
START_CLASS="${START_CLASS:-$DEFAULT_START_CLASS}"
SHUTDOWN_URL="${SHUTDOWN_URL:-$DEFAULT_SHUTDOWN_URL}"
SHUTDOWN_RETRIES="${SHUTDOWN_RETRIES:-$DEFAULT_SHUTDOWN_RETRIES}"
SHUTDOWN_WAIT="${SHUTDOWN_WAIT:-$DEFAULT_SHUTDOWN_WAIT}"
# 动态配置的JVM参数
JVM_PARAMS="${JVM_PARAMS:-}"
# 额外的启动参数
ADDITIONAL_PARAMS="${ADDITIONAL_PARAMS:-$DEFAULT_ADDITIONAL_PARAMS}"
BACKUP_DIR="${BACKUP_DIR:-$DEFAULT_BACKUP_DIR}"  # 配置备份目录

# 配置需要备份的文件/目录，可以通过环境变量来配置
BACKUP_FILES="${BACKUP_FILES:-$DEFAULT_BACKUP_FILES}"


# 可配置的 JVM 参数（默认值可以通过环境变量覆盖）
XMS="${XMS:-2g}"
XMX="${XMX:-8g}"
METASPACE_SIZE="${METASPACE_SIZE:-2g}"

JAR_NAME="$APP_NAME.jar"
PID="$APP_NAME.pid"
APP_HOME=`pwd`
LOG_PATH="$APP_HOME/$DEFAULT_LOG_PATH"
GC_LOG_PATH="$LOG_PATH/gc"
DUMP_DIR="$LOG_PATH/dump"

DEBUG_FLAG="$2"

# 创建日志目录
create_dirs() {
  if [ ! -d "$LOG_PATH" ]; then
    mkdir -p "$LOG_PATH"
  fi

  if [ ! -d "$GC_LOG_PATH" ]; then
    mkdir -p "$GC_LOG_PATH"
  fi

  if [ ! -d "$DUMP_DIR" ]; then
    mkdir -p "$DUMP_DIR"
  fi
}

# 打印使用说明
usage() {
    echo "Usage: sh [springboot2-heat-start].sh [start|stop|fstop|restart|status|dump|clean|backup|load_env] [debug]"
    echo "  start   : Start the application."
    echo "  stop    : Gracefully stop the application."
    echo "  fstop   : Forcefully stop the application."
    echo "  restart : Restart the application."
    echo "  status  : Check if the application is running."
    echo "  dump    : Generate JVM dumps."
    echo "  debug   : Optional flag to enable debug mode."
    echo "  cleanlogs   : Clean up logs and nohup.out."
    echo "  backup  : backup."
    exit 1
}

# 清理日志和nohup.out文件
clean_logs() {
  echo "Cleaning logs and nohup.out..."
  rm -rf "$LOG_PATH"
  rm -f "nohup.out"
  echo "--- Logs and nohup.out cleaned ---"
}

# Debug模式配置
configure_debug() {
  if [ "$DEBUG_FLAG" = "debug" ]; then
    echo -e "\033[33mWarning: Debug mode enabled! This will enable remote debugging and additional logging.\033[0m"
    JVM_PARAMS="$JVM_PARAMS -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$DEBUG_PORT"
  fi
}

# 配置JVM参数
configure_jvm_options() {
  GC_LOG_OPTS="-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:$GC_LOG_PATH/gc-%t.log"
  DUMP_OPTS="-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$LOG_PATH"
  GC_OPTS="-XX:+UseConcMarkSweepGC"
  OTHER_OPTS="-Djava.security.egd=file:/dev/./urandom"

  JVM_OPTS="-server -Xms$XMS -Xmx$XMX -XX:MetaspaceSize=$METASPACE_SIZE $GC_OPTS $GC_LOG_OPTS $DUMP_OPTS $OTHER_OPTS $JVM_PARAMS"
}

# 配置启动方式（拆包启动与否）
configure_start_mode() {
  if [ "$UNPACK" = "true" ]; then
    START_CMD="java -cp "$APP_HOME/$JAR_NAME:lib/*"  $JVM_OPTS $START_CLASS  -Dspring.config.location=$CONFIG_PATH $ADDITIONAL_PARAMS"
  else
    START_CMD="java $JVM_OPTS -jar $APP_HOME/$JAR_NAME -Dspring.config.location=$CONFIG_PATH $ADDITIONAL_PARAMS"
  fi
}

# 检查应用是否正在运行
is_exist() {
  pid=`ps -ef | grep $APP_HOME/$JAR_NAME | grep -v grep | awk '{print $2}'`
  if [ -z "${pid}" ]; then
    return 1
  else
    return 0
  fi
}

# 启动应用
start() {
  create_dirs
  configure_debug
  configure_jvm_options
  configure_start_mode
  echo "Starting applicatibn with the following cmd:$START_CMD"
  if [ "$CONSOLE_LOG" = "true" ]; then
    nohup $START_CMD &
  else
    nohup $START_CMD > /dev/null 2>&1 &
  fi
  echo $! > $PID
  echo "--- Start successful, PID=$! ---"
}

# 查看状态
status() {
  is_exist
  if [ $? -eq "0" ]; then
    echo "--- ${APP_NAME} is running with PID ${pid} ---"
  else
    echo "--- ${APP_NAME} is not running ---"
  fi
}

# 重启应用
restart() {
  stop
  start
}

# 优雅关闭应用
stop() {
  local retries=0
  while [ $retries -lt $SHUTDOWN_RETRIES ]; do
    echo "Attempting to gracefully stop the application (Attempt #$((retries + 1)))..."
    response=$(curl --write-out "%{http_code}" --silent --output /dev/null -X POST $SHUTDOWN_URL)

    if [ "$response" -eq 200 ]; then
      echo "--- ${APP_NAME} gracefully stopped ---"
      break
    fi

    retries=$((retries + 1))
    if [ $retries -ge $SHUTDOWN_RETRIES ]; then
      echo "--- Graceful stop failed after $retries attempts. Force stopping... ---"
      fstop
    else
      echo "--- Graceful stop attempt failed, retrying in $SHUTDOWN_WAIT seconds... ---"
      sleep $SHUTDOWN_WAIT
    fi
  done
}

# 强制停止应用
fstop() {
  pid=$(cat $PID)
  kill -9 $pid
  rm -f $PID
  echo "--- ${APP_NAME} forcefully stopped. PID was ${pid} ---"
}

# 生成JVM dump文件
dump() {
  is_exist
  if [ $? -eq "0" ]; then
    pid=$($ps -ef | grep $APP_HOME/$JAR_NAME | grep -v grep | awk '{print $2}')
    echo "Dumping JVM data for PID $pid"
    jstack $pid > $DUMP_DIR/jstack-$pid.dump
    jmap -heap $pid > $DUMP_DIR/jmap-heap-$pid.dump
    jmap -histo $pid > $DUMP_DIR/jmap-histo-$pid.dump
    echo "Dump completed: $DUMP_DIR"
  else
    echo "--- ${APP_NAME} is not running ---"
  fi
}

# 备份文件：根据配置的文件/目录进行备份
backup_files() {
  TIMESTAMP=$(date +%Y%m%d%H%M%S)
  BACKUP_DIR_TIMESTAMP="$BACKUP_DIR/backup-$TIMESTAMP"
  
  mkdir -p "$BACKUP_DIR_TIMESTAMP"
  
  # 遍历需要备份的文件/目录
  for item in $BACKUP_FILES; do
    if [ -e "$item" ]; then
      echo "Backing up $item"
      mv "$item" "$BACKUP_DIR_TIMESTAMP"
    else
      echo "Warning: $item does not exist and will be skipped."
    fi
  done

  echo "--- Backup completed in $BACKUP_DIR_TIMESTAMP ---"
}


# 根据输入参数执行对应操作
case "$1" in
  "start")
    start
    ;;
  "status")
    status
    ;;
  "restart")
    restart
    ;;
  "dump")
    dump
    ;;
  "stop")
    stop
    ;;
  "fstop")
    fstop
    ;;
  "backup")
    backup_files
    ;;
  "cleanlogs")
    clean_logs
    ;;
  *)
    usage
    ;;
esac

exit 0
